package databases

import (
	"context"
	"fmt"
	"log"
	"regexp"
	"time"

	"github.com/uptrace/opentelemetry-go-extra/otelgorm"
	"github.com/zeromicro/go-zero/core/logx"
	"gorm.io/gorm"
)

// 常量定义
const (
	// 支持的数据库类型
	DatabaseTypeMySQL      = "mysql"
	DatabaseTypePostgreSQL = "postgresql"
	DatabaseTypeSQLite3    = "sqlite3"
	DatabaseTypeClickHouse = "clickhouse"

	// 默认连接池配置
	DefaultMaxOpenConns    = 100
	DefaultMaxIdleConns    = 10
	DefaultConnMaxLifetime = 3600 // 1小时
)

// Database 数据库连接配置信息
type Database struct {
	Name            string `json:"name"`                           // 数据库配置名称
	Type            string `json:"type"`                           // 数据库类型
	Host            string `json:"host"`                           // 数据库服务器主机地址
	Port            int    `json:"port"`                           // 数据库服务器端口号
	Username        string `json:"username"`                       // 数据库连接用户名
	Password        string `json:"password"`                       // 数据库连接密码
	Database        string `json:"database"`                       // 数据库名称
	Charset         string `json:"charset,default=utf8mb4"`        // 数据库字符集编码
	MaxOpenConns    int    `json:"max_open_conns,default=100"`     // 数据库最大打开连接数，go-zero 默认值为100
	MaxIdleConns    int    `json:"max_idle_conns,default=10"`      // 数据库最大空闲连接数，go-zero 默认值为10
	ConnMaxLifetime int    `json:"conn_max_lifetime,default=3600"` // 连接最大生命周期(秒)，go-zero 默认值为3600
	Debug           bool   `json:"debug,default=false"`            // 是否开启调试模式，go-zero默认值为false
	Metrics         bool   `json:"metrics,default=false"`          // 是否启用数据库性能指标，go-zero默认值为false
	ApplicationName string `json:"application_name,optional"`      // 应用名称，go-zero表示可选项
}

// InitDBter 数据库初始化接口
type InitDBter interface {
	InitDB() *gorm.DB
}

// InitDB 初始化数据库连接
func (d *Database) InitDB() *gorm.DB {
	if d == nil {
		panic("数据库配置不能为空")
	}

	// 设置默认值
	d.setDefaults()

	var db *gorm.DB

	// 根据数据库类型选择对应的配置和初始化方式
	switch d.Type {
	case DatabaseTypeMySQL:
		config := MySQLConfig(*d)
		db = config.InitDB()
	case DatabaseTypePostgreSQL:
		config := PostgreSQLConfig(*d)
		db = config.InitDB()
	case DatabaseTypeSQLite3:
		config := SQLiteConfig(*d)
		db = config.InitDB()
	case DatabaseTypeClickHouse:
		config := ClickHouseConfig(*d)
		db = config.InitDB()
	default:
		panic(fmt.Sprintf("不支持的数据库类型: %s", d.Type))
	}

	otelPlugin := otelgorm.NewPlugin(otelgorm.WithDBName(d.Name))

	if err := db.Use(otelPlugin); err != nil {
		panic(err)
	}

	// 配置数据库连接池
	if err := d.configureConnectionPool(db); err != nil {
		panic(fmt.Sprintf("配置数据库连接池失败: %v", err))
	}

	// 如果开启调试模式，则启用gorm的调试功能
	if d.Debug {
		db = db.Debug()
	}

	return db
}

// setDefaults 设置默认配置值
func (d *Database) setDefaults() {
	if d.MaxOpenConns == 0 {
		d.MaxOpenConns = DefaultMaxOpenConns
	}
	if d.MaxIdleConns == 0 {
		d.MaxIdleConns = DefaultMaxIdleConns
	}
	if d.ConnMaxLifetime == 0 {
		d.ConnMaxLifetime = DefaultConnMaxLifetime
	}
}

// configureConnectionPool 配置数据库连接池
func (d *Database) configureConnectionPool(db *gorm.DB) error {
	// 获取底层sql.DB实例以便设置连接池参数
	sqlDB, err := db.DB()
	if err != nil {
		return fmt.Errorf("获取SQL DB实例失败: %w", err)
	}

	// 配置数据库连接池参数
	sqlDB.SetMaxOpenConns(d.MaxOpenConns)
	sqlDB.SetMaxIdleConns(d.MaxIdleConns)
	sqlDB.SetConnMaxLifetime(time.Duration(d.ConnMaxLifetime) * time.Second)

	// 测试数据库连接是否正常
	if err := sqlDB.Ping(); err != nil {
		return fmt.Errorf("数据库连接测试失败: %w", err)
	}

	return nil
}

// QueryCondition 查询条件结构体
type QueryCondition struct {
	Field string            // 查询字段名
	Op    map[string]string // 操作符: "=", "like", ">", "<", ">=", "<=", "<>", "in"
	Value interface{}       // 查询值
}

type QueryInfo struct {
	QueryConditions []QueryCondition
	Limit           uint32
	Page            uint32
}

// AdvancedQueryDatabase 支持复杂查询条件的函数
// 参数:
//   - tx: 数据库连接对象
//   - out: 查询结果存储的目标切片指针
//   - conditions: 查询条件列表
//
// 返回值:
//   - error: 查询过程中可能发生的错误
func AdvancedQueryDatabase[T any](ctx context.Context, tx *gorm.DB, out *[]T, conditions QueryInfo) error {
	query := tx.WithContext(ctx)
	for _, cond := range conditions.QueryConditions {
		// 验证字段名，防止SQL注入
		if !isValidFieldName(cond.Field) {
			logx.Error("无效的字段名")
			return fmt.Errorf("invalid field name: %s", cond.Field)
		}

		// 统一处理操作符
		for op, value := range cond.Op {
			switch op {
			case "Fuzzy":
				// 模糊查询，自动添加通配符
				query = query.Where(fmt.Sprintf("%s LIKE ?", cond.Field), "%"+fmt.Sprintf("%v", cond.Value)+"%")
			case "Precise":
				switch value {
				case "=":
					query = query.Where(fmt.Sprintf("%s = ?", cond.Field), cond.Value)
				case "!=":
					query = query.Where(fmt.Sprintf("%s != ?", cond.Field), cond.Value)
				case ">":
					query = query.Where(fmt.Sprintf("%s > ?", cond.Field), cond.Value)
				case "<":
					query = query.Where(fmt.Sprintf("%s < ?", cond.Field), cond.Value)
				case ">=":
					query = query.Where(fmt.Sprintf("%s >= ?", cond.Field), cond.Value)
				case "<=":
					query = query.Where(fmt.Sprintf("%s <= ?", cond.Field), cond.Value)
				case "in":
					query = query.Where(fmt.Sprintf("%s in ?", cond.Field), cond.Value)
				case "not in":
					query = query.Where(fmt.Sprintf("%s not in ?", cond.Field), cond.Value)
				case "<>":
					query = query.Where(fmt.Sprintf("%s <> ?", cond.Field), cond.Value)
				default:
					query = query.Where(fmt.Sprintf("%s = ?", cond.Field), cond.Value)
				}
			}
		}
	}

	limit := conditions.Limit
	offset := (conditions.Page - 1) * conditions.Limit
	result := query.Limit(int(limit)).Offset(int(offset)).Find(out)
	if result.Error != nil {
		logx.Error("高级查询数据失败")
		return result.Error
	}

	if result.RowsAffected == 0 {
		logx.Info("高级查询完成，无匹配数据")
	} else {
		log.Printf("高级查询数据成功，查询%v条数据", len(*out))
	}
	return nil
}

// isValidFieldName 验证字段名是否合法，防止SQL注入
func isValidFieldName(fieldName string) bool {
	// 字段名只能包含字母、数字和下划线，且不能以数字开头
	match, _ := regexp.MatchString("^[a-zA-Z_][a-zA-Z0-9_]*$", fieldName)
	return match
}

// ... existing code ...

// CreateDatabase 创建数据记录
// 参数:
//   - tx: 数据库连接对象
//   - data: 要插入的数据切片
//
// 返回值:
//   - error: 插入过程中可能发生的错误
func CreateDatabase[T any](ctx context.Context, tx *gorm.DB, data []T) error {
	if err := tx.WithContext(ctx).Create(&data).Error; err != nil {
		log.Printf("插入数据失败，错误信息: %v，数据条数: %v", err, len(data))
		return err
	}
	log.Printf("插入数据成功，插入%v条数据", len(data))
	return nil
}

// DeleteDatabase 删除数据记录
// 参数:
//   - tx: 数据库连接对象
//   - data: 要删除的数据切片
//
// 返回值:
//   - error: 删除过程中可能发生的错误
func DeleteDatabase[T any](ctx context.Context, tx *gorm.DB, data []T) error {
	if err := tx.WithContext(ctx).Delete(&data).Error; err != nil {
		log.Printf("删除数据失败，错误信息: %v，数据条数: %v", err, len(data))
		return err
	}
	log.Printf("删除数据成功，删除%v条数据", len(data))
	return nil
}

// QueryDatabase 根据条件查询数据记录
// 参数:
//   - tx: 数据库连接对象
//   - out: 查询结果存储的目标切片指针
//   - where: 查询条件键值对
//
// 返回值:
//   - error: 查询过程中可能发生的错误
func QueryDatabase[T any](ctx context.Context, tx *gorm.DB, out *[]T, where map[string]interface{}) error {
	result := tx.WithContext(ctx).Where(where).Find(out)
	if result.Error != nil {
		log.Printf("查询数据失败，错误信息: %v，查询条件: %v", result.Error, where)
		return result.Error
	}

	if result.RowsAffected == 0 {
		log.Printf("查询完成，无匹配数据，查询条件: %v", where)
	} else {
		log.Printf("查询数据成功，查询%v条数据，查询条件: %v", len(*out), where)
	}
	return nil
}

// UpdateDatabase 使用给定的条件更新数据库中的记录
// 参数:
//   - ctx: 上下文对象，用于控制请求的生命周期
//   - tx: GORM数据库连接对象，用于执行数据库操作
//   - data: 要更新的数据结构体，包含需要更新的字段值
//   - where: 更新条件映射，指定哪些记录需要被更新
//
// 返回值:
//   - error: 更新操作的错误信息，如果更新成功则返回nil
func UpdateDatabase[T any](ctx context.Context, tx *gorm.DB, data T, where map[string]interface{}) error {
	// 执行数据库更新操作
	result := tx.WithContext(ctx).Where(where).Updates(&data)
	if result.Error != nil {
		log.Printf("Update更新数据失败，错误信息: %v，更新条件: %v\n", result.Error, where)
		return result.Error
	}
	log.Printf("Update更新数据成功，影响行数: %v，更新条件: %v\n", result.RowsAffected, where)
	return nil
}

// SaveDatabase 使用给定的条件保存数据到数据库（不存在则创建，存在则更新）
// 参数:
//   - ctx: 上下文对象，用于控制请求的生命周期
//   - tx: GORM数据库连接对象，用于执行数据库操作
//   - data: 要保存的数据结构体
//   - where: 保存条件映射，指定记录的查找条件
//
// 返回值:
//   - error: 保存操作的错误信息，如果保存成功则返回nil
func SaveDatabase[T any](ctx context.Context, tx *gorm.DB, data T, where map[string]interface{}) error {
	// 执行数据库保存操作
	result := tx.WithContext(ctx).Where(where).Save(&data)
	if result.Error != nil {
		log.Printf("Save更新数据失败，错误信息: %v，更新条件: %v\n", result.Error, where)
		return result.Error
	}
	log.Printf("Save更新数据成功，影响行数: %v，更新条件: %v\n", result.RowsAffected, where)
	return nil
}

// SelectiveUpdateDatabase 支持选择性更新零值字段
// 参数:
//   - tx: 数据库连接对象
//   - data: 要更新的数据对象
//   - where: 更新条件键值对
//   - selectFields: 指定需要更新的字段列表
//
// 返回值:
//   - error: 更新过程中可能发生的错误
func SelectiveUpdateDatabase[T any](ctx context.Context, tx *gorm.DB, data T, where map[string]interface{}, selectFields ...string) error {
	query := tx.WithContext(ctx).Model(&data).Where(where)

	// 如果指定了要更新的字段，则只更新这些字段（包括零值）
	if len(selectFields) > 0 {
		query = query.Select(selectFields)
	}

	result := query.Updates(data)
	if result.Error != nil {
		log.Printf("选择性更新数据失败，错误信息: %v，更新条件: %v\n", result.Error, where)
		return result.Error
	}
	log.Printf("选择性更新数据成功，影响行数: %v，更新条件: %v\n", result.RowsAffected, where)
	return nil
}
